home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
551-575
/
disk_556
/
scheme2c
/
scheme-src.lzh
/
scrt
/
scrt5.sc
< prev
next >
Wrap
Text File
|
1991-10-11
|
18KB
|
524 lines
;;; SCHEME->C Runtime Library
;* Copyright 1989 Digital Equipment Corporation
;* All Rights Reserved
;*
;* Permission to use, copy, and modify this software and its documentation is
;* hereby granted only under the following terms and conditions. Both the
;* above copyright notice and this permission notice must appear in all copies
;* of the software, derivative works or modified versions, and any portions
;* thereof, and both notices must appear in supporting documentation.
;*
;* Users of this software agree to the terms and conditions set forth herein,
;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
;* right and license under any changes, enhancements or extensions made to the
;* core functions of the software, including but not limited to those affording
;* compatibility with other hardware or software environments, but excluding
;* applications which incorporate this software. Users further agree to use
;* their best efforts to return to Digital any such changes, enhancements or
;* extensions that they make and inform Digital of noteworthy uses of this
;* software. Correspondence should be provided to Digital at:
;*
;* Director of Licensing
;* Western Research Laboratory
;* Digital Equipment Corporation
;* 100 Hamilton Avenue
;* Palo Alto, California 94301
;*
;* This software may be distributed (but not offered for sale or transferred
;* for compensation) to third parties, provided such third parties agree to
;* abide by the terms and conditions of this notice.
;*
;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
;* SOFTWARE.
(module scrt5
(top-level
STDIN-PORT STDOUT-PORT STDERR-PORT
CALL-WITH-INPUT-FILE CALL-WITH-OUTPUT-FILE INPUT-PORT? OUTPUT-PORT?
CURRENT-INPUT-PORT CURRENT-OUTPUT-PORT
WITH-INPUT-FROM-FILE WITH-OUTPUT-TO-FILE
OPEN-INPUT-FILE OPEN-OUTPUT-FILE OPEN-FILE MAKE-FILE-PORT
OPEN-INPUT-STRING OPEN-OUTPUT-STRING
CLOSE-INPUT-PORT CLOSE-OUTPUT-PORT CLOSE-PORT
DEFINE-SYSTEM-FILE-TASK WAIT-SYSTEM-FILE ENABLE-SYSTEM-FILE-TASKS))
;;; 6.10. Input and Output
;;;
;;; All I/O is done to and from "ports", where a port is an object that can
;;; read and write characters. A port is represented as (PORT . proc)
;;; where the symbol "PORT" identifies the object, and the procedure "proc"
;;; implements the operations. This is but one example where closures provide
;;; an elegant, simple solution. Needless to say, if a more general object
;;; based system is later implemented, the I/O system should be rewritten
;;; using it.
;;;
;;; The function MAKE-STRING-PORT makes a port which allows expressions to be
;;; read from a string, and MAKE-FILE-PORT makes a port which allows
;;; expressions to be read from a file. Each time an I/O operation is done,
;;; the port's procedure is invoked. It is called with a symbol which is the
;;; method needed and a procedure which performs that method is returned. That
;;; procedure is then called with the appropriate arguments to perform the
;;; operation and return the result. If the desired method does not exist,
;;; then #F should be returned. The required methods for all ports are:
;;;
;;; METHOD ARGUMENTS OPERATION & RESULT
;;;
;;; CLOSE-PORT - close port for all I/O, result is unspecified
;;;
;;; If the port supports input, then it must provide the following methods:
;;;
;;; READ-CHAR - next input character or EOF-OBJECT
;;; PEEK-CHAR - "peek" at the next character, or EOF-OBJECT
;;; CHAR-READY? - boolean indicating that an input character is
;;; available
;;;
;;; Ports which support output must provide the following methods:
;;;
;;; WRITE-CHAR character output the character, result is unspecified
;;; WRITE-TOKEN token output the token (character, string, or list
;;; of characters). If the token will not fit
;;; in the current line, then it will start a new
;;; line.
;;; WRITE-WIDTH - number of characters per line
;;; WRITE-WIDTH! number sets the number of characters per line, result
;;; is unspecified
;;; WRITE-COUNT - number of characters on current line
;;; WRITE-FLUSH - flush buffers, result is unspecified
;;;
;;; Some ports support the following additional methods:
;;;
;;; ECHO - port that I/O is echoed to (or #f)
;;; ECHO! port/#F sets I/O echo port
;;; FILE-PORT - Stdio library FILE for the port.
;;; External declarations for Standard I/O Subroutines
(define-c-external STDIN pointer "sc_stdin")
(define-c-external STDOUT pointer "sc_stdout")
(define-c-external STDERR pointer "sc_stderr")
(define-c-external (FOPEN pointer pointer) pointer "fopen")
(define-c-external (FCLOSE pointer) int "fclose")
(define-c-external (FFLUSH pointer) int "fflush")
(define-c-external (FILENO pointer) int "sc_fileno")
(define-c-external (FGETC pointer) int "fgetc")
(define-c-external (CLEARERR pointer) int "sc_clearerr")
(define-c-external (FEOF pointer) int "sc_feof")
(define-c-external (FERROR pointer) int "sc_ferror")
(define-c-external (FPUTC int pointer) int "fputc")
(define-c-external (INPUT-CHARS? pointer) int "sc_inputchars")
(define-c-external (BUFFERED-CHARS? pointer) int "sc_bufferedchars")
(define-c-external ERRNO int "errno")
(define-c-external LIBC-EOF int "sc_libc_eof")
(define-c-external (SELECT int pointer pointer pointer pointer) int "select")
;;; 6.10.1 Ports
(define (CALL-WITH-INPUT-FILE filename proc)
(if (not (procedure? proc))
(error 'CALL-WITH-INPUT-FILE "Argument is not a PROCEDURE: ~s" proc))
(let* ((port (open-file filename "r"))
(result (proc port)))
(close-port port)
result))
(define (CALL-WITH-OUTPUT-FILE filename proc)
(if (not (procedure? proc))
(error 'CALL-WITH-OUTPUT-FILE "Argument is not a PROCEDURE: ~s" proc))
(let* ((port (open-file filename "w"))
(result (proc port)))
(close-port port)
result))
(define (INPUT-PORT? x)
(if (and (pair? x) (eq? (car x) 'port) (procedure? (cdr x))
((cdr x) 'read-char))
#t
#f))
(define (OUTPUT-PORT? x)
(if (and (pair? x) (eq? (car x) 'port) (procedure? (cdr x))
((cdr x) 'write-char))
#t
#f))
;;; The current input and output ports are kept in the following two cells.
;;; Initially the input port uses stdin and the output port uses stdout.
(define CURRENT-INPUT-PORT-VALUE (make-file-port stdin "r"))
(define CURRENT-OUTPUT-PORT-VALUE (make-file-port stdout "w"))
(define STDIN-PORT current-input-port-value)
(define STDOUT-PORT current-output-port-value)
(define STDERR-PORT (make-file-port stderr "w"))
(define (CURRENT-INPUT-PORT) current-input-port-value)
(define (CURRENT-OUTPUT-PORT) current-output-port-value)
(define (WITH-INPUT-FROM-FILE filename proc)
(let ((old-input-port (current-input-port))
(result '()))
(if (not (procedure? proc))
(error 'WITH-INPUT-FROM-FILE "Argument is not a PROCEDURE: ~s"
proc))
(set! current-input-port-value (open-file filename "r"))
(set! result (proc))
(close-port current-input-port-value)
(set! current-input-port-value old-input-port)
result))
(define (WITH-OUTPUT-TO-FILE filename proc)
(let ((old-output-port (current-output-port))
(result '()))
(if (not (procedure? proc))
(error 'WITH-OUTPUT-TO-FILE "Argument is not a PROCEDURE: ~s"
proc))
(set! current-output-port-value (open-file filename "w"))
(set! result (proc))
(close-port current-output-port-value)
(set! current-output-port-value old-output-port)
result))
(define (OPEN-INPUT-FILE filename) (open-file filename "r"))
(define (OPEN-OUTPUT-FILE filename) (open-file filename "w"))
;;; The following function does the actual file opening. It uses UNIX's fopen
;;; and supports the various open types. See the man page fopen(3s) for
;;; more information. The filename and type are expected to be strings and the
;;; return value of the function is a port.
(define (OPEN-FILE filename type)
(let ((file '()))
(if (not (string? filename))
(error 'FILENAME->FILE "Argument is not a STRING: ~s" filename))
(set! file (fopen filename type))
(if (zero? file)
(error 'FILENAME->FILE "Unable to open file ~s" filename))
(let ((port (make-file-port file type)))
(when-unreferenced port close-port)
port)))
;;; The following function is used to make a port which is does I/O to a UNIX
;;; file. It takes a file pointer (as a Scheme number) and the type string
;;; that was used to fopen the file initially.
(define (MAKE-FILE-PORT file type)
(letrec ((charcnt 0)
(width 80)
(echo-port #f)
(nextchar #f)
(write-char (lambda (char)
(if (char<? char #\space)
(cond ((memq char
'(#\linefeed #\return
#\newline))
(set! charcnt 0))
((eq? char #\tab)
(set! charcnt
(+ charcnt
(- 8
(remainder charcnt
8)))))
(else (set! charcnt (+ charcnt 1))))
(set! charcnt (+ charcnt 1)))
(if (eq? (fputc (char->integer char) file)
libc-eof)
(error 'MAKE-FILE-PORT
"I/O error ~s on output"
(ferror file)))))
(write-token (lambda (token)
(cond ((char? token)
(write-char token))
((or (pair? token) (null? token))
(for-each write-char token))
(else
(let ((len (string-length token)))
(do ((i 0 (+ i 1)))
((= i len))
(write-char
(string-ref
token i))))))))
(read-char (lambda ()
(cond (nextchar
(let ((c nextchar))
(set! nextchar #f)
c))
(else
(if (and (not (eq? system-file-mask 0))
(eq? (buffered-chars? file) 0))
(wait-system-file (fileno file)))
(let ((char (fgetc file)))
(if (eq? char libc-eof)
(if (feof file)
(begin (clearerr file)
$_eof-object)
(error 'MAKE-FILE-PORT
"I/O error ~s on port"
(ferror file)))
(integer->char char)))))))
(peek-char (lambda ()
(if nextchar
nextchar
(set! nextchar (read-char)))))
(read-char-echo (lambda ()
(let ((char (read-char)))
(if (not (eof-object? char))
(((cdr echo-port) 'write-char)
char))
char)))
(char-ready? (lambda ()
(if (or nextchar (eq? (input-chars? file) 1))
#t
#f)))
(close-port (lambda ()
(fflush file)
(fclose file)))
(write-char-echo (lambda (char)
(write-char char)
(((cdr echo-port) 'write-char) char)))
(write-token-echo (lambda (token)
(write-token token)
(((cdr echo-port) 'write-token) token)))
(write-count (lambda () charcnt))
(write-width (lambda () width))
(write-width! (lambda (w) (set! width w)))
(write-flush (lambda () (fflush file)))
(echo (lambda () echo-port))
(echo! (lambda (p) (set! echo-port p)))
(file-port (lambda () file)))
(cond ((equal? type "r")
(set! write-char-echo #f)
(set! write-char #f))
((equal? type "w")
(set! read-char-echo #f)
(set! read-char #f)))
(cons 'port
(lambda (method)
(case method
((close-port) close-port)
((read-char) (if echo-port
read-char-echo
read-char))
((peek-char) peek-char)
((char-ready?) char-ready?)
((write-char) (if echo-port
write-char-echo
write-char))
((write-token) (if echo-port
write-token-echo
write-token))
((write-width) write-width)
((write-width!) write-width!)
((write-count) write-count)
((write-flush) write-flush)
((echo) echo)
((echo!) echo!)
((file-port) file-port)
(else #f))))))
;;; The following function turns a string into an input port and thus allows
;;; Scheme expressions to be read from strings. It is as defined in Chez
;;; Scheme.
(define (OPEN-INPUT-STRING string)
(letrec ((nextchar 0)
(strlen (string-length string))
(read-char (lambda ()
(if (= nextchar strlen)
$_eof-object
(let ((char
(string-ref string nextchar)))
(set! nextchar (+ 1 nextchar))
char))))
(peek-char (lambda () (if (= nextchar strlen)
$_eof-object
(string-ref string nextchar))))
(true (lambda () #t)))
(cons 'port
(lambda (method)
(case method
((read-char) read-char)
((peek-char) peek-char)
((char-ready?) true)
((close-port) true)
(else #f))))))
;;; The following function is used to make a port which does I/O to a string.
;;; It is as defined in Chez Scheme.
(define (OPEN-OUTPUT-STRING)
(letrec ((chars '())
(width 80)
(write-token (lambda (token)
(cond ((char? token)
(set! chars (cons token chars)))
((or (pair? token) (null? token))
(set! chars
(append (reverse token)
chars)))
(else
(set! chars
(append (reverse (string->list
token))
chars))))))
(get-output-string (lambda ()
(let ((s (list->string
(reverse chars))))
(set! chars '())
s)))
(write-char (lambda (char)
(set! chars (cons char chars))))
(write-width (lambda () width))
(write-width! (lambda (w) (set! width w)))
(write-count (lambda () (length chars))))
(cons 'port
(lambda (method)
(case method
((write-token) write-token)
((write-char) write-char)
((write-width) write-width)
((write-width!) write-width!)
((write-count) write-count)
((get-output-string) get-output-string)
(else #f))))))
(define (CLOSE-INPUT-PORT port) (close-port port))
(define (CLOSE-OUTPUT-PORT port) (close-port port))
(define (CLOSE-PORT port)
(if (and (not (input-port? port)) (not (output-port? port)))
(error 'CLOSE-PORT "Argument is not a PORT: ~s" port))
(when-unreferenced port #f)
(((cdr port) 'close-port)))
;;; When there are no characters available on a port, the I/O system executes
;;; the idle task associated with each system file and then dispatches system
;;; tasks or continues reading from the port when some read completes. N.B:
;;; (1) System file tasks never interrupt an executing Scheme program.
;;; (2) System file tasking is disabled while in the debugger.
;;; (3) All pending system file tasks are executed before continuing reads
;;; from the port.
(define SYSTEM-TASKING #t)
(define SYSTEM-FILE-MASK 0)
(define MAX-SYSTEM-FILE -1)
(define SYSTEM-FILE-TASK #f)
(define IDLE-TASKS (make-vector 32 #f))
(define FILE-TASKS (make-vector 32 #f))
;;; A task is associated with a system file number by the following procedure.
;;; A task is deleted by passing #F for each task procedure.
(define (DEFINE-SYSTEM-FILE-TASK file idle-task file-task)
(vector-set! idle-tasks file idle-task)
(vector-set! file-tasks file file-task)
(set! system-file-mask 0)
(set! max-system-file -1)
(do ((i 0 (+ 1 i)))
((= i 32))
(when (vector-ref file-tasks i)
(set! max-system-file i)
(set! system-file-mask (bit-or system-file-mask (bit-lsh 1 i)))))
file)
;;; A task waits for input on a file by calling the following procedure with
;;; the system file number, or #f. When input is available on the file (<> #f)
;;; or all tasks have completed, the procedure returns.
(define (WAIT-SYSTEM-FILE file)
(if (and (not (eq? system-file-mask 0)) system-tasking)
(let ((x (make-string 4)))
(if (eq? file 0) (flush-buffer stdout-port))
(do ((i 0 (+ i 1)))
((> i max-system-file))
(if (vector-ref idle-tasks i) ((vector-ref idle-tasks i))))
(c-unsigned-set! x 0
(bit-or system-file-mask (if file (bit-lsh 1 file) 0)))
(if (eq? (select (+ (max max-system-file (or file 0)) 1) x 0 0 0)
-1)
(if (eq? errno 4)
(wait-system-file file)
(error 'wait-system-file "Select error: ~s" errno)))
(let ((inputs (c-unsigned-ref x 0)))
(do ((i 0 (+ i 1))
(mask 1 (bit-lsh mask 1)))
((> i max-system-file))
(if (not (eq? 0 (bit-and mask inputs)))
(let ((task (vector-ref file-tasks i)))
(when task
(set! system-file-task i)
(task)))))
(set! system-file-task #f)
(if (or (not file) (zero? (bit-and inputs (bit-lsh 1 file))))
(wait-system-file file))))))
;;; System file tasking is enabled and disabled by the following procedure.
;;; It returns the previous state of system file tasking. When called with
;;; WAIT as its argument, it will not return until all system file tasks have
;;; completed.
(define (ENABLE-SYSTEM-FILE-TASKS enable)
(let ((prev system-tasking))
(set! system-tasking (if enable #t #f))
(if (eq? enable 'wait) (wait-system-file #f))
prev))